﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Net;
using JZElectrick.Properties;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics;

namespace JZElectrick {
    public enum EBServiceSavingType {
        SavePassword,
        AutoLoginWhenAppLaunch,
        AutoLoginWhenStartUp,
        None
    }

    public class EBService {
        bool bg = false;

        string baseUrl = "https://ecard.jluzh.com/";
        string codeUrl = null;
        string loginUrl = null;
        string listUrl = null;
        string balanceUrl = null;

        string tempCodeImgFilePath = null;
        string tempCodeResFilePath = null;
        string balanceDataPath = null;

        CookieContainer cookie = null;

        string savedUsername = null;
        string savedPassword = null;

        string[] buildList = null;
        public Dorm dorm = null;

        public delegate void ShowLoginDelegate(string username, string psw, string code, Image codeImg);
        public ShowLoginDelegate ShowLoginEvent;

        public delegate void LoginFailedDelegate(string msg);
        public LoginFailedDelegate LoginFailedEvent;

        public delegate void LoginSuccessDelegate();
        public LoginSuccessDelegate LoginSuccessEvent;

        public delegate void ShowDormDelegate(string[] list);
        public ShowDormDelegate ShowDormEvent;

        public delegate void FinishLoadBalance(string[] balance);
        public FinishLoadBalance FinishLoadBalanceEvent;

        public delegate void NoNeedForBGRunDelegate();
        public NoNeedForBGRunDelegate NoNeedForBGRunEvent;

        public EBService() {
            codeUrl = baseUrl + "checkCode.action";
            loginUrl = baseUrl + "login.action";
            listUrl = baseUrl + "utilitRoomQueryXiaoQuData.action";
            balanceUrl = baseUrl + "utilitRoomQueryBalance.action";

            tempCodeImgFilePath = Application.StartupPath + "\\temp\\code.png";
            tempCodeResFilePath = Application.StartupPath + "\\temp\\result";
            balanceDataPath = Application.StartupPath + "\\balance";

            Console.WriteLine("codeImgpath: " + tempCodeImgFilePath);
            Console.WriteLine("resultPath: " + tempCodeResFilePath);

            if(!Directory.Exists(Application.StartupPath + "\\temp")) {
                Directory.CreateDirectory(Application.StartupPath + "\\temp");
            }
        }

        public EBService(bool bg): this() {
            this.bg = bg;
        }

        public void Start() {
            //((Action)delegate () {
            //    System.Threading.Thread.Sleep(3000);
            //    ShowLoginEvent(null, null, null, null);
            //}).BeginInvoke(null, null);

            if (bg) {
                if (!BGRunCheck()) {
                    Console.WriteLine("no need to run");
                    NoNeedForBGRunEvent();
                    return;
                }
            }

            if (ValidateCookie()) {
                if (dorm == null) {
                    dorm = LocalDorm();
                }
                if (dorm == null) {
                    ShowDormEvent(buildList);
                    return;
                }
                return;
            }

            LoadCode();
        }
        
        private bool BGRunCheck() {
            try {
                if (dorm == null) {
                    dorm = LocalDorm();
                }
                if (dorm == null) {
                    return true;
                }
                FileStream fs = new FileStream(balanceDataPath, FileMode.OpenOrCreate);
                StreamReader sr = new StreamReader(fs);
                string jsonstr = sr.ReadToEnd();
                sr.Close();
                fs.Close();
                JObject jObj = JsonConvert.DeserializeObject(jsonstr) as JObject;
                if (jObj == null) {
                    return true;
                }
                //2017-6-19 10:00:00

                JToken roomObj = jObj[dorm.RoomName];
                if (roomObj == null) {
                    return true;
                } else {
                    string timeStr = DateTime.Now.ToString("yyyy-M-d") + " 10:00:00";
                    JToken val = roomObj[timeStr];
                    if (val == null) {
                        return true;
                    } else {
                        return false;
                    }
                }
            } catch(Exception ex) {
                Console.WriteLine(ex.StackTrace);
                return true;
            }
            
        }

        private bool ValidateCookie() {
            CookieContainer localCookie = Settings.Default.cookie;
            if (localCookie != null) {
                cookie = localCookie;
            }
            if (buildList == null) {
                buildList = LoadList(null, null);
            }
            if (buildList == null) {
                return false;
            }
            return true;
        }

        private Dorm LocalDorm() {
            string[] dormData = new string[7];
            dormData[0] = Settings.Default.dormFullName;
            dormData[1] = Settings.Default.dormBuildName;
            dormData[2] = Settings.Default.dormFloorName;
            dormData[3] = Settings.Default.dormRoomName;
            dormData[4] = Settings.Default.dormBuildID;
            dormData[5] = Settings.Default.dormFloorID;
            dormData[6] = Settings.Default.dormRoomID;

            foreach (string item in dormData) {
                if (item == null || item == "") {
                    return null;
                }
            }

            return new Dorm(dormData[0], dormData[1], dormData[2], dormData[3], dormData[4], dormData[5], dormData[6]);
        }
        
        public void LoadCode() {
            try {
                Image image = Image.FromStream(HttpGetStream(codeUrl));
                image.Save(tempCodeImgFilePath, ImageFormat.Png);
                string result = OcrCode();
                PerpareLogin(result, image);

            } catch(Exception ex) {
                Console.WriteLine(ex.StackTrace);
            }
        }
        

        private string OcrCode() {
            //TODO: 
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = "cmd.exe";
            startInfo.CreateNoWindow = true;
            startInfo.UseShellExecute = false;
            startInfo.RedirectStandardOutput = true;
            startInfo.RedirectStandardInput = true;
            Process process = Process.Start(startInfo);
            process.StandardInput.WriteLine("\"" + Application.StartupPath + "\\ocr\\tesseract.exe\" " + "\"" + tempCodeImgFilePath +"\" \"" + tempCodeResFilePath + "\"");
            process.StandardInput.WriteLine("exit");
            process.WaitForExit();
            process.Close();


            if (File.Exists(tempCodeResFilePath + ".txt")) {
                StreamReader sr = new StreamReader(tempCodeResFilePath + ".txt", Encoding.Default);
                string code = sr.ReadLine();
                sr.Close();

                if (File.Exists(tempCodeResFilePath + ".txt")) {
                    File.Delete(tempCodeResFilePath + ".txt");
                }

                return code;
            } else {
                return null;
            }
        }

        private void PerpareLogin(string codeResult, Image imgData) {
            string username = Settings.Default.UserName;
            Console.WriteLine("savedUsername:  " + username);
            if (username == null || username == "") {
                ShowLoginEvent(null, null, codeResult, imgData);
                return;
            }

            string psw = Settings.Default.Psw;

            ShowLoginEvent(username, psw, codeResult, imgData);
        }

        public void DoLogin(string username, string password, string code) {
            Dictionary<string, string> dict = new Dictionary<string, string>();
            dict.Add("loginMode", "");
            dict.Add("username", username);
            dict.Add("password", password);
            dict.Add("checkCode", code);

            string result = HttpPost(loginUrl, dict);
            if (result == null) {
                LoginFailedEvent("网络好像有点问题，请检查网络连接");
                return;
            }
            //Console.WriteLine("loginResult: " + result);
            
            string[] list = LoadList(null, null);
            if (list == null) {
                LoginFailedEvent("登陆失败，请检查用户名密码验证码是否正确");
                return;
            }
            buildList = list;
            LoginSuccessEvent();
            savedUsername = username;
            savedPassword = password;
            Settings.Default.UserName = username;
            //Settings.Default.Psw = password;
            
            Settings.Default.Save();

        }

        public string[] LoadList(string buildID, string floorID) {
            Dictionary<string, string> dict = new Dictionary<string, string>();
            dict.Add("dormId", "1");
            dict.Add("dormName", "吉林大学珠海学院");

            if (buildID != null) {
                dict.Add("buildingId", buildID);
                if (floorID != null) {
                    dict.Add("floorId", floorID);
                }
            }
            
            string result = HttpPost(listUrl, dict);
            if (result == null) {
                return null;
            }
            Console.WriteLine("listResult: " + result);
            string[] list = result.Split('|');
            if (list.Length < 2) {
                return null;
            }
            return list;
        }

        public string[] LoadBalance() {
            if (buildList == null) {
                LoginFailedEvent("登陆失败，请检查用户名密码验证码是否正确");
                return null;
            }

            if (dorm == null) {
                dorm = LocalDorm();
            }

            if (dorm == null) {
                ShowDormEvent(buildList);
                return null;
            }

            Dictionary<string, string> dict = new Dictionary<string, string>();
            dict.Add("dormId", "1");
            dict.Add("dormName", "吉林大学珠海学院");
            dict.Add("buildingId", dorm.BuildID);
            dict.Add("floorId", dorm.FloorID);
            dict.Add("roomId", dorm.RoomID);

            string balanceData = HttpPost(balanceUrl, dict);
            Console.WriteLine(balanceData);
            if (balanceData == null) {
                FinishLoadBalanceEvent(null);
                return null;
            }
            string[] balance = balanceData.Split(',');
            if (balance.Length < 2) {
                FinishLoadBalanceEvent(null);
                return null;
            }


            if (bg) {
                FinishLoadBalanceEvent(balance);
                SaveBalance(balance);
            } else {
                SaveBalance(balance);
                FinishLoadBalanceEvent(balance);
            }
            


            return balance;
        }

        public void SaveState(EBServiceSavingType type) {
            Settings.Default.FirstLaunch = false;

            switch (type) {
                case EBServiceSavingType.SavePassword:
                    Settings.Default.Psw = savedPassword;
                    Settings.Default.AutoLogin = false;
                    Settings.Default.cookie = new CookieContainer();
                    break;
                case EBServiceSavingType.AutoLoginWhenAppLaunch:
                    Settings.Default.Psw = savedPassword;
                    Settings.Default.AutoLogin = true;
                    Settings.Default.cookie = cookie;
                    break;
                case EBServiceSavingType.AutoLoginWhenStartUp:
                    Settings.Default.Psw = savedPassword;
                    Settings.Default.AutoLogin = true;
                    Settings.Default.cookie = cookie;
                    break;
                case EBServiceSavingType.None:
                    Settings.Default.Psw = "";
                    Settings.Default.AutoLogin = false;
                    Settings.Default.cookie = new CookieContainer();
                    
                    break;
                default:
                    break;
            }
            Settings.Default.Save();
        }
        
        public void SaveDorm(Dorm dorm) {
            this.dorm = dorm;
            Settings.Default.dormFullName = dorm.FullName;
            Settings.Default.dormBuildName = dorm.BulidName;
            Settings.Default.dormFloorName = dorm.FloorName;
            Settings.Default.dormRoomName = dorm.RoomName;
            Settings.Default.dormBuildID = dorm.BuildID;
            Settings.Default.dormFloorID = dorm.FloorID;
            Settings.Default.dormRoomID = dorm.RoomID;
            Settings.Default.Save();
        }

        private void SaveBalance(string[] balance) {
            try {
                FileStream fs = new FileStream(balanceDataPath, FileMode.OpenOrCreate);
                StreamReader sr = new StreamReader(fs);
                string jsonstr = sr.ReadToEnd();
                sr.Close();
                fs.Close();
                JObject jObj = JsonConvert.DeserializeObject(jsonstr) as JObject;
                if (jObj == null) {
                    jObj = new JObject();

                }

                JToken roomObj = jObj[dorm.RoomName];
                if (roomObj == null) {
                    roomObj = new JObject();
                    roomObj[balance[1]] = balance[0];
                    jObj[dorm.RoomName] = roomObj;
                } else {
                    JToken val = roomObj[balance[1]];
                    if (val == null) {
                        roomObj[balance[1]] = balance[0];
                    }

                }
                    

                string newJsonStr = jObj.ToString();
                Console.WriteLine(newJsonStr);

                StreamWriter sw = new StreamWriter(balanceDataPath);
                sw.Write(newJsonStr);
                sw.Close();
            } catch(Exception ex) {
                Console.WriteLine(ex.StackTrace);
            }
            
        }

        #region HTTP 基础函数

        public string HttpGet(string url) {
            try {
                
                StreamReader sr = new StreamReader(HttpGetStream(url), Encoding.UTF8);

                string result = sr.ReadToEnd();
                sr.Close();

                return result;

            } catch(Exception ex) {
                Console.WriteLine(ex.StackTrace);
                return null;
            }
        }

        public Stream HttpGetStream(string url) {
            try {
                HttpWebRequest resq = (HttpWebRequest)WebRequest.Create(url);
                resq.Method = "GET";

                if (cookie == null) {
                    cookie = new CookieContainer();
                }
                resq.CookieContainer = cookie;

                HttpWebResponse resp = (HttpWebResponse)resq.GetResponse();
                return resp.GetResponseStream();

            } catch (Exception ex) {
                Console.WriteLine(ex.StackTrace);
                return null;
            }
        }

        public string HttpPost(string url, Dictionary<string,string> postBody) {
            try {
                HttpWebRequest resq = (HttpWebRequest)WebRequest.Create(url);
                resq.Method = "POST";
                resq.ContentType = "application/x-www-form-urlencoded";
                if (cookie == null) {
                    cookie = new CookieContainer();
                }
                resq.CookieContainer = cookie;
                
                if (postBody != null) {
                    string postData = "";

                    foreach (string key in postBody.Keys) {
                        string value = postBody[key];
                        if (postData != "") {
                            postData += "&";
                        }
                        postData += key + "=" + value;
                    }

                    byte[] byteArray = Encoding.UTF8.GetBytes(postData);
                    resq.ContentLength = byteArray.Length;

                    Stream requestStream = resq.GetRequestStream();
                    requestStream.Write(byteArray, 0, byteArray.Length);
                    requestStream.Close();
                }

                HttpWebResponse response = (HttpWebResponse)resq.GetResponse();
                StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
                string result = sr.ReadToEnd();
                sr.Close();
                response.Close();

                return result;

            } catch(Exception ex) {
                Console.WriteLine(ex.StackTrace);
                return null;
            }
        }

        #endregion 
    }
}
